﻿

Public Class HDCurveControl

#Region " 构造函数 "

    Private _pi As ProjectItem
    Private _rti As RefTypeItem

    Public Sub New()

        ' 此调用是 Windows 窗体设计器所必需的。
        InitializeComponent()

        ' 在 InitializeComponent() 调用之后添加任何初始化。

    End Sub

#End Region

#Region " 成员变量 "

    Private _g As Graphics
    Private _gPrint As Graphics

    Private _bmp As Bitmap
    Private _bmpPrint As Bitmap

    Private _bgColor As Color = Color.Black
    Private _bgColor4Print As Color = Color.White

    Private _foreColor As Color = Color.White
    Private _foreColor4Print As Color = Color.Black

    Private _forePen As New Pen(Color.White)
    Private _forePen4Print As New Pen(Color.Black)

    Private _foreBrush As New SolidBrush(Color.White)
    Private _foreBrush4Print As New SolidBrush(Color.Black)

    Private _Color_Zone() As Color = {Color.DarkRed, Color.DarkGoldenrod, Color.DarkGreen, Color.DarkBlue, Color.DarkOrange, Color.DeepPink, Color.Red, Color.Goldenrod, Color.Green, Color.Blue, Color.Orange, Color.Pink}

    Private Const SCALECOUNT As Integer = 5

    '曲线点距；最小值1，最大值根据曲线拉伸情况计算
    Private _DotPitch As Decimal = 1

    Private _aWidth As Integer = 720
    Private _aHeight As Integer = 500
    Private _aTop As Integer = 20
    Private _aLeft As Integer = 20
    Private _aBottom As Integer = 20
    Private _aRight As Integer = 20

    Private _aLeft4CD As Integer = _aLeft + 20
    Private _aMarkLeft As Integer = 40
    Private _aMarkWidth As Integer = 120

    Private _hasTemp As Boolean
    Private _hasHumi As Boolean

    '温湿度最大最小值
    Private _TempMaxLimit As Integer = 50
    Private _TempMinLimit As Integer = 0
    Private _HumiMaxLimit As Integer = 100
    Private _HumiMinLimit As Integer = 0

    '温湿度上下限
    Private _TempUpperLimit As Integer = 30
    Private _TempLowerLimit As Integer = 20
    Private _HumiUpperLimit As Integer = 80
    Private _HumiLowerLimit As Integer = 40

    '温湿度坐标区域
    Private _THC_Area As System.Drawing.Rectangle

    Private _l As New List(Of RTDValueInfo)
    Private _dl As New List(Of RTData)

    Private _BeginTime As DateTime
    Private _EndTime As DateTime
    Private _DataInval As Integer = 10

    Private _IsLoading As Boolean = True
    Private _IsLoadBG As Boolean

#End Region

#Region " 字段属性 "

    Public Property TempMaxLimit() As Integer
        Get
            Return _TempMaxLimit
        End Get
        Set(ByVal value As Integer)
            _TempMaxLimit = value
        End Set
    End Property

    Public Property TempMinLimit() As Integer
        Get
            Return _TempMinLimit
        End Get
        Set(ByVal value As Integer)
            _TempMinLimit = value
        End Set
    End Property

    Public Property HumiMaxLimit() As Integer
        Get
            Return _HumiMaxLimit
        End Get
        Set(ByVal value As Integer)
            _HumiMaxLimit = value
        End Set
    End Property

    Public Property HumiMinLimit() As Integer
        Get
            Return _HumiMinLimit
        End Get
        Set(ByVal value As Integer)
            _HumiMinLimit = value
        End Set
    End Property

    Public Property TempUpperLimit() As Integer
        Get
            Return _TempUpperLimit
        End Get
        Set(ByVal value As Integer)
            _TempUpperLimit = value
        End Set
    End Property

    Public Property TempLowerLimit() As Integer
        Get
            Return _TempLowerLimit
        End Get
        Set(ByVal value As Integer)
            _TempLowerLimit = value
        End Set
    End Property

    Public Property HumiUpperLimit() As Integer
        Get
            Return _HumiUpperLimit
        End Get
        Set(ByVal value As Integer)
            _HumiUpperLimit = value
        End Set
    End Property

    Public Property HumiLowerLimit() As Integer
        Get
            Return _HumiLowerLimit
        End Get
        Set(ByVal value As Integer)
            _HumiLowerLimit = value
        End Set
    End Property

    Public Property RefTypeInfo() As RefTypeItem
        Get
            Return _rti
        End Get
        Set(ByVal value As RefTypeItem)
            _rti = value
        End Set
    End Property

#End Region

#Region " 公共方法 "

    Public Sub InitCurveControl()
        InitControlImage()
        InitBitmap()
        InitBG(_g)
    End Sub

    Public Sub RunDrawCurve(ByVal il As IList(Of HDataTypeItem), ByVal beginTime As DateTime, ByVal endTime As DateTime)

        _BeginTime = beginTime
        _EndTime = endTime

        SetCurveData(il)

        Me.bwMain.RunWorkerAsync()

    End Sub

    Private Sub SetCurveData(ByVal il As IList(Of HDataTypeItem))

        _dl.Clear()

        Dim dt As DateTime
        Dim im As Decimal

        im = ((_EndTime - _BeginTime).TotalSeconds / _DataInval) / _aWidth

        For Each vi As RTDValueInfo In _l
            Dim v(24 * 60) As Single
            For j As Integer = 0 To v.Length - 1
                v(j) = Single.NaN
            Next

            Dim i As Integer = 0
            For Each hdti As HDataTypeItem In il

                dt = hdti.RecordTime

                i = (dt - _BeginTime).TotalSeconds / _DataInval

                If Math.Round(i Mod im) <> 0 Then
                    Continue For
                End If

                'i = i / im

                Select Case vi.ID
                    Case 1
                        v(i) = hdti.AI1
                    Case 2
                        v(i) = hdti.AI2
                    Case 3
                        v(i) = hdti.AI3
                    Case 4
                        v(i) = hdti.AI4

                End Select

            Next

            Dim item As New RTData()
            item._bufSingle = v
            item.rtdItem = vi

            _dl.Add(item)
        Next

    End Sub

#End Region

#Region " 消息处理 "

    Private Sub HDCurveControl_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        GetDataAreaSize()

        'InitControlImage()
        'InitBitmap()
        'InitBG(_g)

        _IsLoading = False

    End Sub

    Private Sub HDCurveControl_Resize(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Resize

        If _IsLoading = False AndAlso _IsLoadBG = True Then
            GetDataAreaSize()

            InitBitmap()
            InitBG(_g)

            If Me.bwMain.IsBusy = False Then
                Me.bwMain.RunWorkerAsync()
            End If
        End If

    End Sub

    Private Sub bwMain_DoWork(ByVal sender As System.Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bwMain.DoWork

        DoWork(e)

    End Sub

    Private Sub bwMain_ProgressChanged(ByVal sender As System.Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles bwMain.ProgressChanged

        ProgressChanged(e)

    End Sub

    Private Sub bwMain_RunWorkerCompleted(ByVal sender As System.Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bwMain.RunWorkerCompleted

        RunWorkerCompleted(e)

    End Sub

#End Region

#Region " 处理方法 "

    Private Sub GetDataAreaSize()

        _aWidth = Me.Width - (_aLeft4CD + _aRight + _aMarkLeft + _aMarkWidth)
        _aHeight = Me.Height - (_aTop + _aBottom)

    End Sub

    Private Sub InitControlImage()
        Dim portnum As Integer = 0
        _hasTemp = False
        _hasHumi = False

        _l.Clear()

        If _rti Is Nothing Then Exit Sub

        _TempMaxLimit = _rti.HDCurveTempUp
        _TempMinLimit = _rti.HDCurveTempDown
        _HumiMaxLimit = _rti.HDCurveHumiUp
        _HumiMinLimit = _rti.HDCurveHumiDown

        For Each apti As AiPortTypeItem In _rti.AiPortTypeGroupList
            If String.IsNullOrEmpty(apti.PortName) = False AndAlso apti.PortNo <> 0 Then '如果AI名称不为空并且端口号不为0
                'portnum += 1
                If apti.DataType = 1 Then '温度
                    _l.Add(CreateRTDValueInfo(apti.PortNo, apti.PortName, _TempMaxLimit, _TempMinLimit, _Color_Zone(apti.PortNo - 1), apti.DataType))
                    If _hasTemp = False Then
                        portnum += 1
                        _hasTemp = True
                    End If
                ElseIf apti.DataType = 2 Then '湿度
                    _l.Add(CreateRTDValueInfo(apti.PortNo, apti.PortName, _HumiMaxLimit, _HumiMinLimit, _Color_Zone(apti.PortNo - 1), apti.DataType))
                    If _hasHumi = False Then
                        portnum += 1
                        _hasHumi = True
                    End If
                End If
            End If
        Next

        '根据需要显示的温湿度的数量调整曲线的坐标
        '_aWidth = 720
        '_aHeight = 500
        _aTop = 20
        _aLeft4CD = 40 * portnum + _aLeft

        GetDataAreaSize()
        GetTHCArea()

        'InitBG(_g) '刷新图层但不显示数据
        'Me.pbMain.Image = _bmp

    End Sub

    Private Function CreateRTDValueInfo(ByVal id As Integer, ByVal name As String, ByVal maxvalue As Integer, ByVal minvalue As Integer, ByVal c As Color, ByVal dataType As Integer) As RTDValueInfo
        Dim vi As New RTDValueInfo()
        vi.ID = id
        vi.Name = name
        vi.maxValue = maxvalue
        vi.minValue = minvalue
        vi.color = c
        vi.length = 2
        vi.dataType = dataType
        Return vi
    End Function

    Private Sub InitBitmap()
        _bmp = New Bitmap(Me.Width, Me.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)
        Me.pbMain.Image = _bmp
        _g = Graphics.FromImage(_bmp)
        _g.Clear(Color.Black)
    End Sub

    Private Sub InitBG(ByVal g As Graphics)
        _IsLoadBG = True
        Me.labLine.Visible = False
        Me.labInfo.Visible = False
        Me.labLine.Size = New Size(1, _aHeight)
        g.Clear(_bgColor)
        DrawMark(g)
        DrawCoordinates(g)
        DrawXCoordinate(g, False)
        DrawYCoordinate(g)
        SetCurLabPara()
        SetInfoLabPara()
    End Sub

    Private Sub GetTHCArea()
        _THC_Area = New Rectangle(_aLeft, _aTop, _aLeft4CD - _aLeft, _aHeight)
    End Sub

#Region " 曲线函数 "

    Private Sub DrawMark(ByVal g As Graphics)
        Dim markTop As Integer = 25
        Dim height As Integer = 25
        Dim left As Integer = _aLeft4CD + _aWidth + _aMarkLeft

        Dim rMark As New Rectangle(left, _aTop, _aMarkWidth, height * _l.Count)
        g.DrawRectangle(_forePen, rMark)

        Dim font As Font = New Font(Me.Font.FontFamily, 10)

        Dim iRow As Integer = 0
        For Each item As RTDValueInfo In _l
            Dim r As New Rectangle(left + 20, iRow * height + markTop + 1, 12, 12)
            Dim b As Brush = New SolidBrush(item.color)
            g.FillRectangle(b, r)
            Dim s As String
            s = item.Name
            g.DrawString(s, font, _foreBrush, left + 50, iRow * height + markTop)
            iRow += 1
        Next
    End Sub

    Private Sub DrawCoordinates(ByVal g As Graphics)
        Dim r As New Rectangle(_aLeft4CD, _aTop, _aWidth, _aHeight)
        g.DrawRectangle(_forePen, r)
        'g.DrawLine(_forePen, aLeft, aTop + aHeight \ 2, aLeft + aWidth, aTop + aHeight \ 2)
        g.DrawLine(_forePen, _aLeft4CD + _aWidth, _aTop + _aHeight, _aLeft4CD + _aWidth + 15, _aTop + _aHeight)
        g.FillPolygon(New SolidBrush(_forePen.Color), New Point() {New Point(_aLeft4CD + _aWidth + 15, _aTop + _aHeight), _
                                                                New Point(_aLeft4CD + _aWidth + 5, _aTop + _aHeight + 3), _
                                                                New Point(_aLeft4CD + _aWidth + 5, _aTop + _aHeight - 3) _
                                                    } _
                            )
    End Sub

    Private Sub DrawXCoordinate(ByVal g As Graphics, ByVal isPrint As Boolean)
        'If Me._dl.Count = 0 Then
        '    Exit Sub
        'End If

        'Dim index As Integer
        'index = Me._dl(0).CurrentIndex
        Dim tWidth As Single = 30 '每格宽度
        Dim fb As Brush = _foreBrush
        If isPrint Then
            fb = _foreBrush4Print
        End If

        Dim dt As DateTime
        dt = Me.GetCurrentTime

        Dim p As New Pen(Brushes.Gray, 2)
        Dim p1 As New Pen(Brushes.DarkGray, 2)

        Dim sTime As String

        Dim dtT, dtF As DateTime
        Dim im As Decimal = 1
        Dim base, length As Integer
        base = 1 '1小时1格

        dtT = _EndTime
        dtF = _BeginTime
        length = (dtT - dtF).TotalHours

        If length > 0 Then
            base = Math.Ceiling(length / 24)
            If base > 1 AndAlso base Mod 2 <> 0 Then base += 1
            im = 24 / length
            tWidth = _aWidth / length
        End If

        For i As Integer = 0 To length
            'If Math.Round(i Mod im) = 0 Then
            If (dtF.Hour + i) Mod base = 0 Then
                If i <> 0 AndAlso i <> length Then
                    sTime = ((dtF.Hour + i) Mod 24).ToString("D2") 'Math.Round(i / im)
                    g.DrawString(sTime, Me.Font, fb, GetX(i * tWidth) - 8, _aTop + _aHeight + 5)
                End If

                If ((dtF.Hour + i) Mod 24) = 0 Then
                    p1.Width = 2
                    g.DrawLine(p1, GetX(i * tWidth), _aTop, GetX(i * tWidth), _aTop + _aHeight)
                Else
                    p.Width = 1
                    g.DrawLine(p, GetX(i * tWidth), _aTop, GetX(i * tWidth), _aTop + _aHeight)
                End If
            End If

            If i = 0 Then
                'If (dtF.Hour Mod base) <= base / 2 Then
                sTime = ((dtF.Hour + i) Mod 24).ToString("D2") 'Math.Round(i / im)
                g.DrawString(sTime, Me.Font, fb, GetX(i * tWidth) - 15, _aTop + _aHeight + 5)
                'End If
                p.Width = 1
                g.DrawLine(p, GetX(i * tWidth), _aTop, GetX(i * tWidth), _aTop + _aHeight)
                If (24 - dtF.Hour) * tWidth >= 90 Then
                    g.DrawString(dtF.ToLongDateString, Me.Font, fb, GetX(i * tWidth) - 50, _aTop + _aHeight + 20)
                End If
            ElseIf ((dtF.Hour + i) Mod 24) = 0 Then
                g.DrawString(dtF.AddDays(i / 24).ToLongDateString, Me.Font, fb, GetX(i * tWidth) - 50, _aTop + _aHeight + 20)
            End If

            If i = length Then
                p.Width = 1
                g.DrawLine(p, GetX(_aWidth), _aTop, GetX(_aWidth), _aTop + _aHeight)
                sTime = ((dtF.Hour + i) Mod 24).ToString("D2") 'Math.Round(i / im)
                'If (dtF.Hour + i) Mod base = 0 Then
                '    g.DrawString(sTime, Me.Font, _foreBrush, GetX(720) - 8, aTop + aHeight + 5)
                'Else
                g.DrawString(sTime, Me.Font, fb, GetX(_aWidth), _aTop + _aHeight + 5)
                'End If
                g.DrawString(String.Format("小时(×{0})", base), Me.Font, Brushes.Gray, GetX(_aWidth + 30), _aTop + _aHeight + 5)
            End If
        Next

        p.Dispose()
        p1.Dispose()

    End Sub

    Private Sub DrawYCoordinate(ByVal g As Graphics)
        Dim gap As Integer = 40

        Dim x As Integer = _aLeft4CD
        Dim iRow As Integer = 0
        Dim index As Integer = 0
        If _hasTemp Then

            index += 1

            x -= gap
            Dim p As New Pen(Color.Orange)
            Dim b As New SolidBrush(Color.Orange)

            Dim yd As Integer = 0

            g.DrawLine(p, x, _aTop + yd - 15, x, _aTop + _aHeight + yd)
            g.FillPolygon(New SolidBrush(p.Color), New Point() {New Point(x, _aTop + yd - 15), _
                                                                New Point(x - 3, _aTop + yd - 5), _
                                                                New Point(x + 3, _aTop + yd - 5) _
                                                    } _
                            )
            g.DrawString("℃", Me.Font, b, x + 5, _aTop + yd - 15)

            g.DrawLine(p, x, _aTop + yd, x + gap, _aTop + yd)
            g.DrawLine(p, x, _aTop + _aHeight + yd, x + gap, _aTop + _aHeight + yd)

            'g.DrawString("℃", Me.Font, New SolidBrush(p.Color), GetX(770), aTop - 15)

            'If index Mod 2 = 0 Then
            '    yd = -12
            'End If

            Dim kd As Integer = _aHeight / SCALECOUNT
            Dim s As String
            For k As Integer = 0 To SCALECOUNT
                s = Math.Round(_TempMaxLimit - k * (_TempMaxLimit - _TempMinLimit) / SCALECOUNT, 2)
                g.DrawString(s, Me.Font, b, x, _aTop + k * kd + yd)
                g.DrawLine(p, x - 2, _aTop + k * kd, x + 2, _aTop + k * kd)
            Next

            p.Dispose()
            b.Dispose()

        End If

        If _hasHumi Then

            index += 1

            x -= gap
            Dim p As New Pen(Color.LightBlue)
            Dim b As New SolidBrush(Color.LightBlue)

            Dim yd As Integer = 0

            g.DrawLine(p, x, _aTop + yd - 15, x, _aTop + _aHeight + yd)
            g.FillPolygon(New SolidBrush(p.Color), New Point() {New Point(x, _aTop + yd - 15), _
                                                                New Point(x - 3, _aTop + yd - 5), _
                                                                New Point(x + 3, _aTop + yd - 5) _
                                                    } _
                            )
            g.DrawString("%", Me.Font, b, x + 5, _aTop + yd - 15)

            g.DrawLine(p, x, _aTop + yd, x + gap, _aTop + yd)
            g.DrawLine(p, x, _aTop + _aHeight + yd, x + gap, _aTop + _aHeight + yd)

            'g.DrawString("％", Me.Font, New SolidBrush(p.Color), GetX(750), aTop - 15)

            'If index Mod 2 = 0 Then
            '    yd = -12
            'End If

            Dim kd As Integer = _aHeight / SCALECOUNT
            Dim s As String
            For k As Integer = 0 To SCALECOUNT
                s = Math.Round(_HumiMaxLimit - k * (_HumiMaxLimit - _HumiMinLimit) / SCALECOUNT, 2)
                g.DrawString(s, Me.Font, b, x, _aTop + k * kd + yd)
                g.DrawLine(p, x - 2, _aTop + k * kd, x + 2, _aTop + k * kd)
            Next

            p.Dispose()
            b.Dispose()

        End If

        Dim y As Integer = 0
        For i As Integer = 1 To SCALECOUNT - 1
            y += _aHeight / SCALECOUNT
            'g.DrawLine(Me._forePen, aLeft, aTop + y, aLeft + aWidth, aTop + y)
            g.DrawLine(Pens.Gray, x, _aTop + y, _aLeft4CD + _aWidth, _aTop + y)
        Next

    End Sub

    Private Function GetX(ByVal x As Integer) As Integer
        Return _aLeft4CD + x
    End Function

    Private Function GetY(ByVal y As Single, ByVal r As RTDValueInfo) As Integer
        Dim rn As New Random
        'y = rn.Next(1, 120)

        Dim t As Integer

        'todo:y值无效
        y = y Mod 20000

        Try
            t = _aHeight + _aTop - ((y - r.minValue) / (r.maxValue - r.minValue)) * _aHeight '- (r.maxValue + r.minValue) / 2
        Catch ex As Exception
            Debug.WriteLine(ex.ToString)
            Throw ex
        End Try


        'Debug.WriteLine(r.Name & ": " & y & " - " & t)
        Return t
        'Return 10 + y / 20
    End Function


    Private Function GetY(ByVal y As Single) As Integer
        Return 10 + y / 20
    End Function

    Private Function GetCurrentTime() As DateTime
        Return _EndTime 'dtpTo.Value
    End Function

    Private Sub DrawLine(ByVal g As Graphics)
        Dim SwitchCount As Integer = 0

        For Each d As RTData In _dl

            If d.rtdItem.length = 0 Then
                '开关量
                SwitchCount += 1
                Dim yy As Integer = _aTop + _aHeight - SwitchCount * 12

                Dim p As New Pen(d.rtdItem.color, 10)
                Dim p2 As New Pen(d.rtdItem.color, 1)
                For i As Integer = 1 To d.CurrentIndex
                    If d._bufByte(i) Mod 2 = 0 Then
                        g.DrawLine(p2, GetX(i - 1), yy, GetX(i), yy)
                    Else
                        g.DrawLine(p, GetX(i - 1), yy, GetX(i), yy)
                    End If
                Next
                p.Dispose()
                p2.Dispose()

            Else
                '曲线

                Dim p As New Pen(d.rtdItem.color)
                Dim pv, cv As Single
                'Dim ii As Integer = 0
                For i As Integer = 1 To d._bufSingle.Length - 1
                    If Not (Single.IsNaN(d._bufSingle(i - 1)) OrElse Single.IsNaN(d._bufSingle(i))) Then
                        'ii += 1
                        pv = d._bufSingle(i - 1)
                        cv = d._bufSingle(i)
                        'If pv >= d.rtdItem.minValue AndAlso pv <= d.rtdItem.maxValue Then
                        If pv <= d.rtdItem.maxValue Then
                            If pv >= d.rtdItem.minValue Then
                                If cv <= d.rtdItem.maxValue Then
                                    If cv >= d.rtdItem.minValue Then
                                        _g.DrawLine(p, GetX(i - 1), GetY(pv, d.rtdItem), GetX(i), GetY(cv, d.rtdItem))
                                    Else
                                        _g.DrawLine(p, GetX(i - 1), GetY(pv, d.rtdItem), GetX(i), GetY(Math.Max(cv, d.rtdItem.minValue), d.rtdItem))
                                    End If
                                Else
                                    _g.DrawLine(p, GetX(i - 1), GetY(pv, d.rtdItem), GetX(i), GetY(Math.Min(cv, d.rtdItem.maxValue), d.rtdItem))
                                End If
                            Else
                                If cv >= d.rtdItem.minValue Then
                                    _g.DrawLine(p, GetX(i - 1), GetY(Math.Max(pv, d.rtdItem.minValue), d.rtdItem), GetX(i), GetY(cv, d.rtdItem))
                                End If
                            End If
                        Else
                            If cv <= d.rtdItem.maxValue Then
                                If cv >= d.rtdItem.minValue Then
                                    _g.DrawLine(p, GetX(i - 1), GetY(Math.Min(pv, d.rtdItem.maxValue), d.rtdItem), GetX(i), GetY(cv, d.rtdItem))
                                Else
                                    _g.DrawLine(p, GetX(i - 1), GetY(Math.Min(pv, d.rtdItem.maxValue), d.rtdItem), GetX(i), GetY(Math.Max(cv, d.rtdItem.minValue), d.rtdItem))
                                End If
                            End If
                        End If
                        '_g.DrawLine(p, GetX(i - 1), GetY(d._bufSingle(i - 1), d.rtdItem), GetX(i), GetY(d._bufSingle(i), d.rtdItem))
                        'End If
                    End If
                Next
                p.Dispose()
            End If
        Next
    End Sub

#End Region

#Region " 刻度线 "

    Private tempBase, humiBase, timeBase As Single
    Private tempEnd, humiEnd As Integer
    Private timeStart As DateTime
    Private strLabCur As String
    Private strLabInfo As String
    Private curDateTime As DateTime
    Private curInfo(11) As Single

    Private Sub SetCurLabPara()
        tempEnd = _TempMaxLimit
        humiEnd = _HumiMaxLimit
        timeStart = _BeginTime

        tempBase = (_TempMaxLimit - _TempMinLimit) / (_aHeight)
        humiBase = (_HumiMaxLimit - _HumiMinLimit) / (_aHeight)
        timeBase = (_EndTime - _BeginTime).TotalSeconds / (_aWidth)

        strLabCur = "日期：{0}" & vbCrLf
        strLabCur &= "时间：{1}" & vbCrLf

        If _hasTemp Then
            strLabCur &= "温度：{2:F2} ℃" & vbCrLf
        End If

        If _hasHumi Then
            strLabCur &= "湿度：{3:F2} %" & vbCrLf
        End If
    End Sub

    Private Sub SetInfoLabPara()
        Dim index As Integer = 2

        strLabInfo = "日期：{0}" & vbCrLf
        strLabInfo &= "时间：{1}" & vbCrLf

        For Each item As RTDValueInfo In _l
            Select Case item.dataType
                Case 1
                    strLabInfo &= item.Name & "：{" & index & ":F2} ℃" & vbCrLf
                Case 2
                    strLabInfo &= item.Name & "：{" & index & ":F2} %" & vbCrLf
            End Select
            index += 1
        Next
    End Sub

    Private Sub FreshCurInfo(ByVal i As Integer)
        Dim index As Integer = 0

        For Each item As RTData In _dl
            curInfo(index) = item._bufSingle(i)
            index += 1
        Next
    End Sub

    'mouse
    Private Sub pbMain_MouseMove(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles pbMain.MouseMove

        If Me._IsLoadBG = True AndAlso Me.pcProgress.Visible = False Then
            If e.X >= _aLeft4CD AndAlso e.Y >= _aTop AndAlso e.X <= _aLeft4CD + _aWidth AndAlso e.Y <= _aTop + _aHeight Then
                Dim curDT As DateTime
                Dim curDate, curTime As String

                curDT = timeStart.AddSeconds((e.X - _aLeft4CD) * timeBase)
                curDate = curDT.ToShortDateString()
                curTime = curDT.ToLongTimeString()

                '指针位置与信息
                labCur.Text = String.Format(strLabCur, _
                                            curDate, _
                                            curTime, _
                                            Math.Round(tempEnd - (e.Y - _aTop) * tempBase, 2), _
                                            Math.Round(humiEnd - (e.Y - _aTop) * humiBase, 2) _
                                            )
                labCur.Location = New Point(e.X + 15, e.Y + 15)
                labCur.Visible = True

                '刻度线信息
                If e.Button = Windows.Forms.MouseButtons.Left Then
                    keyIndex = e.X - _aLeft4CD
                    FreshCurInfo(keyIndex)
                    labInfo.Text = String.Format(strLabInfo, curDate, curTime, curInfo(0), curInfo(1), curInfo(2), curInfo(3), curInfo(4), curInfo(5), _
                                                                      curInfo(6), curInfo(7), curInfo(8), curInfo(9), curInfo(10), curInfo(11))
                    labLine.Location = New Point(e.X + Me.pbMain.Location.X, _aTop + Me.pbMain.Location.Y)
                    labLine.Visible = True
                End If

                '刻度线位置
                If labLine.Visible = True Then
                    'labInfo.Location = New Point(labCur.Location.X, labCur.Location.Y + labCur.Size.Height + 5)
                    labInfo.Location = New Point(_aLeft4CD + _aWidth + 15, _aTop + _aHeight - labInfo.Size.Height)
                    labInfo.Visible = True
                End If

            Else
                labCur.Visible = False
                If e.Button = Windows.Forms.MouseButtons.Left Then
                    labLine.Visible = False
                    labInfo.Visible = False
                End If
            End If
        End If

    End Sub

    Private Sub pbMain_MouseClick(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles pbMain.MouseClick
        Me.pbMain.Focus()

        If Me._IsLoadBG = True AndAlso Me.pcProgress.Visible = False Then
            If e.X >= _aLeft4CD AndAlso e.Y >= _aTop AndAlso e.X <= _aLeft4CD + _aWidth AndAlso e.Y <= _aTop + _aHeight Then

                curDateTime = timeStart.AddSeconds((e.X - _aLeft4CD) * timeBase)

                '刻度线位置与信息
                keyIndex = e.X - _aLeft4CD
                FreshCurInfo(keyIndex)
                labInfo.Text = String.Format(strLabInfo, curDateTime.ToShortDateString, curDateTime.ToLongTimeString, _
                                                                      curInfo(0), curInfo(1), curInfo(2), curInfo(3), curInfo(4), curInfo(5), _
                                                                      curInfo(6), curInfo(7), curInfo(8), curInfo(9), curInfo(10), curInfo(11))
                labLine.Location = New Point(e.X + Me.pbMain.Location.X, _aTop + Me.pbMain.Location.Y)
                'labInfo.Location = New Point(labCur.Location.X, labCur.Location.Y + labCur.Size.Height + 5)
                labInfo.Location = New Point(_aLeft4CD + _aWidth + 15, _aTop + _aHeight - labInfo.Size.Height)
                labLine.Visible = True
                labInfo.Visible = True

            Else
                labLine.Visible = False
                labInfo.Visible = False
            End If
        End If

    End Sub

    'keyboard
    Private keyIndex As Integer
    Private Sub pbMain_PreviewKeyDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PreviewKeyDownEventArgs) Handles pbMain.PreviewKeyDown
        If Me._IsLoadBG = True Then
            Select Case e.KeyCode
                Case Keys.Left
                    LabLine_Forward(1)

                Case Keys.Right
                    LabLine_Backward(1)

                Case Keys.Up
                    LabLine_Forward(5)

                Case Keys.Down
                    LabLine_Backward(5)

                Case Keys.PageUp
                    LabLine_Forward(10)

                Case Keys.PageDown
                    LabLine_Backward(10)

                Case Keys.Home
                    keyIndex = 0
                    FreshLabInfo(keyIndex)
                    labLine.Location = New Point(_aLeft4CD + keyIndex + Me.pbMain.Location.X, _aTop + Me.pbMain.Location.Y)
                    labInfo.Location = New Point(_aLeft4CD + _aWidth + 15, _aTop + _aHeight - labInfo.Size.Height)
                    labLine.Visible = True
                    labInfo.Visible = True

                Case Keys.End
                    keyIndex = _aWidth
                    FreshLabInfo(keyIndex)
                    labLine.Location = New Point(_aLeft4CD + keyIndex + Me.pbMain.Location.X, _aTop + Me.pbMain.Location.Y)
                    labInfo.Location = New Point(_aLeft4CD + _aWidth + 15, _aTop + _aHeight - labInfo.Size.Height)
                    labLine.Visible = True
                    labInfo.Visible = True

                Case Keys.Escape
                    labLine.Visible = False
                    labInfo.Visible = False

            End Select
        End If
    End Sub

    Private Sub FreshLabInfo(ByVal index As Integer)

        curDateTime = timeStart.AddSeconds((index) * timeBase)

        FreshCurInfo(index)
        labInfo.Text = String.Format(strLabInfo, curDateTime.ToShortDateString, curDateTime.ToLongTimeString, _
                                                 curInfo(0), curInfo(1), curInfo(2), curInfo(3), curInfo(4), curInfo(5), _
                                                 curInfo(6), curInfo(7), curInfo(8), curInfo(9), curInfo(10), curInfo(11))

    End Sub

    Private Sub LabLine_Forward(ByVal [step] As Integer)
        If labLine.Visible = False Then
            keyIndex = _aWidth
            FreshLabInfo(keyIndex)
            labLine.Location = New Point(_aLeft4CD + keyIndex + Me.pbMain.Location.X, _aTop + Me.pbMain.Location.Y)
            labInfo.Location = New Point(_aLeft4CD + _aWidth + 15, _aTop + _aHeight - labInfo.Size.Height)
            labLine.Visible = True
            labInfo.Visible = True
        Else
            If keyIndex > 0 Then
                keyIndex -= Math.Min([step], keyIndex)
                FreshLabInfo(keyIndex)
                labLine.Location = New Point(_aLeft4CD + keyIndex + Me.pbMain.Location.X, _aTop + Me.pbMain.Location.Y)
            Else
                keyIndex = _aWidth
                FreshLabInfo(keyIndex)
                labLine.Location = New Point(_aLeft4CD + keyIndex + Me.pbMain.Location.X, _aTop + Me.pbMain.Location.Y)
            End If
        End If

    End Sub

    Private Sub LabLine_Backward(ByVal [step] As Integer)
        If labLine.Visible = False Then
            keyIndex = 0
            FreshLabInfo(keyIndex)
            labLine.Location = New Point(_aLeft4CD + keyIndex + Me.pbMain.Location.X, _aTop + Me.pbMain.Location.Y)
            labInfo.Location = New Point(_aLeft4CD + _aWidth + 15, _aTop + _aHeight - labInfo.Size.Height)
            labLine.Visible = True
            labInfo.Visible = True
        Else
            If keyIndex < _aWidth Then
                keyIndex += Math.Min([step], (_aWidth - keyIndex))
                FreshLabInfo(keyIndex)
                labLine.Location = New Point(_aLeft4CD + keyIndex + Me.pbMain.Location.X, _aTop + Me.pbMain.Location.Y)
            Else
                keyIndex = 0
                FreshLabInfo(keyIndex)
                labLine.Location = New Point(_aLeft4CD + keyIndex + Me.pbMain.Location.X, _aTop + Me.pbMain.Location.Y)
            End If
        End If
    End Sub

#End Region

    Private Sub DoWork(ByVal e As System.ComponentModel.DoWorkEventArgs)

        DrawLine(_g)

    End Sub

    Private Sub ProgressChanged(ByVal e As System.ComponentModel.ProgressChangedEventArgs)

        SetProgressValue(e.ProgressPercentage)

    End Sub

    Private Sub RunWorkerCompleted(ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs)

        SetImage(_bmp)

    End Sub

#End Region

#Region " 线程安全 "

    '设置进度条数值
    Private Sub SetProgressValue(ByVal value As Integer)
        If Me.InvokeRequired Then
            Me.Invoke(New Action(Of Integer)(AddressOf _SetProgressValue), value)
        Else
            _SetProgressValue(value)
        End If
    End Sub
    Private Sub _SetProgressValue(ByVal value As Integer)
        Me.pcProgress.Value = value
    End Sub

    '设置进度状态显示
    Private Sub SetProgressStatus(ByVal status As String)
        If Me.InvokeRequired Then
            Me.Invoke(New Action(Of String)(AddressOf _SetProgressStatus), status)
        Else
            _SetProgressStatus(status)
        End If
    End Sub
    Private Sub _SetProgressStatus(ByVal status As String)
        Me.pcProgress.StatusText = status
    End Sub

    '设置PIC控件图层
    Private Sub SetImage(ByVal bmp As Bitmap)
        If Me.InvokeRequired Then
            Me.Invoke(New Action(Of Bitmap)(AddressOf _SetImage), bmp)
        Else
            _SetImage(bmp)
        End If
    End Sub
    Private Sub _SetImage(ByVal bmp As Bitmap)
        Me.pbMain.Image = bmp
    End Sub

#End Region

End Class
